#include "precompiled.hpp"
#include "jam_lists.hpp"

const jam_list null_list;

//TODO: add thread safety to node cache (possibly non-blocking struct?)
jam_list_node *node_cache=NULL;
class cache_cleanup
{
public:
	~cache_cleanup()
	{
		while(node_cache)
		{
			jam_list_node *cur=node_cache;
			node_cache=node_cache->next_;
			delete cur;
		}
	}
};
cache_cleanup cleaner;

void free_node(jam_list_node *p)
{
	assert(p->refcount_==1);
	p->next_=node_cache;
	node_cache=p;
}

void free_node_chain(jam_list_node *head,jam_list_node *tail)
{
	assert(head->refcount_==1);
	tail->next_=node_cache;
	node_cache=head;
}

inline jam_list_node* make_new_node()
{	
	if (!node_cache)
		return new jam_list_node();
	else
	{
		jam_list_node *cur=node_cache;
		node_cache=cur->next_;

		//We're reviving a dead node
		assert(cur->refcount_==1);		
		cur->next_=0;
				
		return cur;
	}
}

jam_list::jam_list(const jam_list &other) : size_()
{
	copy_from(other);
}

jam_list& jam_list::operator = (const jam_list &other)
{
	if (&other==this) return *this;
	clear();
	copy_from(other);
	return *this;
}

jam_list& jam_list::operator = (const jam_string &str)
{
	clear();
	value_=str;
	size_=1;
	tail_=this;
	return *this;
}

void jam_list::copy_from(const jam_list &other)
{
	if (other.empty()) return;
	push_back(other.front());
	if (other.next_)
	{		
		next_=other.next_;
		++next_->refcount_;
		tail_=other.tail_;
	}
	size_=other.size_;
}

void jam_list::make_unique()
{
	if (!next_ || next_->refcount_==1) //Check if we're the exclusive owner
		return;
	
	assert(false);
	//Make a private copy of list data
	//TODO: optmize to capture a whole node chain. This should be MUCH faster in MT-mode.
	jam_list_node *new_first=make_new_node(); //TODO: add exception safety
	jam_list_node *cur=next_; //Existing nodes
	jam_list_node *cur_new=new_first; 
	while(true)
	{
		cur_new->value_=cur->value_;
		cur=cur->next_;
		if (!cur) break;

		cur_new->next_=make_new_node();
		cur_new=cur_new->next_;
	}	
	int res=--next_->refcount_; //Decrease refcount
	assert(res!=0);

	tail_=cur_new;
	next_=new_first;
}

bool jam_list::operator == (const jam_list &other) const
{
	const_iterator o_f=other.begin(),o_e=other.end();
	for(const_iterator f=begin();f!=end();++f)
	{
		if (o_f==o_e) return false;
		if (!(*o_f==*f)) return false;
		++o_f;
	}
	if (o_f!=o_e) return false;
	return true;
}

void jam_list::push_back(const jam_string &val)
{
	if (empty())
	{
		assert(!next_);
		value_=val;
		tail_=this;
	} else
	{
		make_unique();
		jam_list_node *cur=make_new_node();
		cur->value_=val;
		tail_->next_=cur;
		tail_=cur;
	}
	size_++;
}

void jam_list::pop_front()
{
	assert(!empty());
	if (size_==1)
		clear();
	else
	{
		if (tail_==next_)
			tail_=this;
		value_=next_->value_;
		jam_list_node *new_next=next_->next_;
		
		if (next_->refcount_==1) //Free node if we're its exclusive owner.
			free_node(next_);
		else
			next_->refcount_--;
		
		assert(new_next->refcount_==0);
		new_next->refcount_=1;
		next_=new_next;
	}
	size_--;
}

void jam_list::move_tail_from(jam_list &other)
{
	if (other.empty()) return;

	//Append the first value
	push_back(other.value_);
	assert(!empty());

	if (other.next_)
	{
		other.make_unique();
		tail_->next_=other.next_;
		tail_=other.tail_;
		other.next_->refcount_++;
	}
	size_+=other.size()-1;
	other.clear();
}

void jam_list::clear()
{
	//We DO NOT clear the node value as an optimization trick.
	//value_.clear(); //Should I do this for debugging purposes?
	if (next_)
	{
		if (next_->refcount_==1)
			free_node_chain(next_,tail_);
		else
			--next_->refcount_;
	}
	next_=tail_=NULL;
	size_=0;
}

jam_string& jam_list::front()
{
	assert(!empty());
	return value_;
}

const jam_string& jam_list::front() const
{
	assert(!empty());
	return value_;
}

jam_string& jam_list::back()
{
	assert(!empty());
	make_unique();
	return tail_->value_;
}

const jam_string& jam_list::back() const
{
	assert(!empty());	
	return tail_->value_;
}

std::ostream& operator << (std::ostream &str,const jam_list &lst)
{
	for(jam_list::const_iterator f=lst.begin();f!=lst.end();)
	{
		str<<*f;
		if ((++f)!=lst.end()) str<<" ";
	}
	return str;
}

std::ostream& operator << (std::ostream &str,const list_of_lists &lst)
{
	for(size_t f=0,fend=lst.size();f!=fend;++f)
	{
		if (f!=0) str<<": ";
		str<<lst[f];
		if (f!=(fend-1)) str<<" ";
	}
	return str;
}
